#include "stdafx.h"
#include "vfwgrab.h"
#include "MainFrame.h"
#include "FTPTransferDlg.h"


const UINT CAPTURE_WND_ID = 100;

//Use of DEBUG_NEW is not compatible with the overriden new operator in GDI+
/*
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
*/

IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

CMainFrame::CMainFrame()
{
  //Read in the settings to use
  CWinApp* pApp = AfxGetApp();
  CString sSection(_T("General"));
  m_nDevice           = pApp->GetProfileInt(sSection, _T("DeviceIndex"), 0);
  m_sDevice           = pApp->GetProfileString(sSection, _T("DeviceName"));
  m_nWidth            = pApp->GetProfileInt(sSection, _T("Width"), 0);
  m_nHeight           = pApp->GetProfileInt(sSection, _T("Height"), 0);
  m_sUNCFile          = pApp->GetProfileString(sSection, _T("File"), 0);
  m_sServer           = pApp->GetProfileString(sSection, _T("Server"));
  m_sRemoteFile       = pApp->GetProfileString(sSection, _T("RemoteFile"));
  m_nPort             = (INTERNET_PORT) pApp->GetProfileInt(sSection, _T("Port"), 21);
  m_sUser             = pApp->GetProfileString(sSection, _T("User"));
  m_sPassword         = pApp->GetProfileString(sSection, _T("Password"));
  m_nTimeInterval     = pApp->GetProfileInt(sSection, _T("TimerInterval"), 1000);
  m_nJpegQuality      = pApp->GetProfileInt(sSection, _T("JpegQuality"), 70);
  m_nPeriodicInterval = pApp->GetProfileInt(sSection, _T("PeriodicInterval"), -1);
  m_nMaxCaptures      = pApp->GetProfileInt(sSection, _T("MaxCaptures"), -1);
  m_bIncrementFilename = (BOOL) pApp->GetProfileInt(sSection, _T("IncrementFilename"), FALSE);

  m_nStartupTimerID = 0;
  m_nPeriodicTimerID = 0;
  m_nCurrentCapture = 0;
}

CMainFrame::~CMainFrame()
{
}

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_COMMAND(ID_FILE_CAPTURE, OnFileCapture)
	ON_WM_CREATE()
  ON_WM_TIMER()
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

int CMainFrame::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
  UINT nNum = 0;          //number of image encoders
  UINT nSize = 0;         //size of the image encoder array in bytes
  Gdiplus::GetImageEncodersSize(&nNum, &nSize);
  if (nSize == 0)
    return -1;  // Failure

  Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*) new BYTE[nSize];
  Gdiplus::GetImageEncoders(nNum, nSize, pImageCodecInfo);
  for (UINT j=0; j<nNum; ++j)
  {
    if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
    {
      *pClsid = pImageCodecInfo[j].Clsid;
      delete [] pImageCodecInfo;
      return j;  // Success
    }    
  }

  delete [] pImageCodecInfo;
  return -1;  // Failure
}

void CMainFrame::OnFileCapture() 
{
  USES_CONVERSION;

  //Generate a temp filename
  TCHAR pszTempPath[_MAX_PATH];
  VERIFY(GetTempPath(_MAX_PATH, pszTempPath));
  TCHAR pszTempFile1[_MAX_PATH];
  if (GetTempFileName(pszTempPath, _T("VWG"), 0, pszTempFile1) == 0)
  {
    AfxMessageBox(_T("Failed to generate temp filename"));
    return;
  }

  //Force a repaint of the window if minimized
	if (IsIconic())
  { 
    Invalidate();
    UpdateWindow();
  }

  //Capture the image to the temp file
  CWaitCursor wait;
  if (!m_wndCap.SaveDib(pszTempFile1))
  {
    CString sError;
    sError.Format(_T("Failed to save image to temp file, %s"), pszTempFile1);
    AfxMessageBox(sError);
    return;
  }

  //Get the CLSID of the JPEG encoder.
  CLSID clsidJpegEncoded;
  if (GetEncoderClsid(L"image/jpeg", &clsidJpegEncoded) == -1)
  {
    DeleteFile(pszTempFile1);

    AfxMessageBox(_T("Failed to obtain the JPEG GDI+ encoder"));
    return;
  }

  //Load the temp DIB from file
  Gdiplus::Image* pImage = new Gdiplus::Image(T2W(pszTempFile1));
  if (pImage->GetLastStatus() != Gdiplus::Ok)
  {
    delete pImage;
    DeleteFile(pszTempFile1);

    CString sError;
    sError.Format(_T("Failed to load image from location, %s"), pszTempFile1);
    AfxMessageBox(sError);
    return;
  }

  //setup the Encoder parameters structure
  Gdiplus::EncoderParameters encoderParameters;
  encoderParameters.Count = 1;
  encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
  encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
  encoderParameters.Parameter[0].NumberOfValues = 1;
  ULONG nJpegQuality = m_nJpegQuality;
  encoderParameters.Parameter[0].Value = &nJpegQuality;

  //Save to the specified location
  if (!m_sUNCFile.IsEmpty())
  {
    //Work out the filename to upload to
    CString sFile;
    if (m_bIncrementFilename)
    {
      TCHAR sPath[_MAX_PATH];
      TCHAR sDir[_MAX_DIR];
      TCHAR sDrive[_MAX_DRIVE];
      TCHAR sFname[_MAX_FNAME];
      TCHAR sExt[_MAX_EXT];
      _tsplitpath(m_sUNCFile, sDrive, sDir, sFname, sExt);
      sFile.Format(_T("%s%d"), sFname, m_nCurrentCapture);
      _tmakepath(sPath, sDrive, sDir, sFile, sExt);
      sFile = sPath;
    }
    else
      sFile = m_sUNCFile;

    if (pImage->Save(T2W((LPTSTR) (LPCTSTR) sFile), &clsidJpegEncoded, &encoderParameters) != Gdiplus::Ok)
    {
      CString sError;
      sError.Format(_T("Failed to save image to location, %s"), sFile);
      AfxMessageBox(sError);
    }      
  }
  else
  {
    TCHAR pszTempFile2[_MAX_PATH];
    if (GetTempFileName(pszTempPath, _T("VWG"), 0, pszTempFile2) == 0)
    {
      delete pImage;
      DeleteFile(pszTempFile1);

      AfxMessageBox(_T("Failed to generate temp filename"));
      return;
    }

    if (pImage->Save(T2W(pszTempFile2), &clsidJpegEncoded, &encoderParameters) == Gdiplus::Ok)
    {
      //Validate the parameters 
      if (m_sServer.GetLength() && m_sRemoteFile.GetLength())
      {
        //Do the FTP transfer
        CFTPTransferDlg dlg;
        dlg.m_bDownload = FALSE; //we're doing an upload
        dlg.m_bPromptOverwrite = FALSE; //Don't ask to overwrite
        dlg.m_sServer = m_sServer;

        //Work out the filename to upload to
        if (m_bIncrementFilename)
        {
          TCHAR sPath[_MAX_PATH];
          TCHAR sDir[_MAX_DIR];
          TCHAR sDrive[_MAX_DRIVE];
          TCHAR sFname[_MAX_FNAME];
          TCHAR sExt[_MAX_EXT];
          _tsplitpath(m_sRemoteFile, sDrive, sDir, sFname, sExt);
          CString sFile;
          sFile.Format(_T("%s%d"), sFname, m_nCurrentCapture);
          _tmakepath(sPath, sDrive, sDir, sFile, sExt);
          dlg.m_sRemoteFile = sPath;
        }
        else
          dlg.m_sRemoteFile = m_sRemoteFile;

        dlg.m_sLocalFile = pszTempFile2;
        dlg.m_sUserName = m_sUser;
        dlg.m_sPassword = m_sPassword;
        dlg.m_nPort = m_nPort;  
        dlg.m_bBinary = TRUE;
        dlg.DoModal();
      }
      else
        AfxMessageBox(_T("Please specify a valid \"Server\"/\"RemoteFile\" combination or \"File\" value(s) in the ini file"));

      DeleteFile(pszTempFile2);
    }
    else
    {
      CString sError;
      sError.Format(_T("Failed to save image to location, %s"), m_sUNCFile);
      AfxMessageBox(sError);
    }
  }

  //Destroy the image now that we are finished with it (in the process will release the lock it has 
  //on the file, which will allow the call to DeleteFile to succeed)
  delete pImage;

  //Delete the temp files
  DeleteFile(pszTempFile1);
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
  //Let the parent class do its thing
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;

  //Check the width parameter
  if (m_nWidth == 0)
  {
    AfxMessageBox(_T("Please specify a \"m_nWidth\" value in the ini file"));
    return -1;
  }

  //Check the height parameter
  if (m_nHeight == 0)
  {
    AfxMessageBox(_T("Please specify a \"m_nHeight\" value in the ini file"));
    return -1;
  }

  //Create the capture window
  if (!m_wndCap.Create(WS_CHILD | WS_VISIBLE, CRect(0, 0, m_nWidth, m_nHeight), this, CAPTURE_WND_ID))
  {
    CString sError;
    sError.Format(_T("Failed to create the VFW Window, Error:%d"), GetLastError());
    AfxMessageBox(sError);
    return -1;
  }

  //Find the specified device index if the name is given
  if (m_sDevice.GetLength())
  {
    //Enumerate all the VFW devices and find the matching index
    m_nDevice = -1;
    for (WORD i=0; i<10 && m_nDevice == -1; i++)
    {
      CString sName, sVersion;
      if (CAVICapWnd::GetDescription(i, sName, sVersion) && m_sDevice.CompareNoCase(sName) == 0)
        m_nDevice = i;
    }
    if (m_nDevice == -1)
    {
      CString sError;
      sError.Format(_T("Could not find the VFW device, %s"), m_sDevice);
      AfxMessageBox(sError);
      return -1;
    }
  }

  //Connect to the specfied device
  if (!m_wndCap.Connect(m_nDevice))
  {
    CString sError;
    sError.Format(_T("Could not connect to the VFW device with index %d"), m_nDevice);
    AfxMessageBox(sError);
    return -1;
  }

  //Set the video format to 24 bit RGB
  BITMAPINFO* pBmi = NULL;
  m_wndCap.GetVideoFormat(pBmi);
  ASSERT(pBmi);
  pBmi->bmiHeader.biWidth = m_nWidth;
  pBmi->bmiHeader.biHeight = m_nHeight;
  pBmi->bmiHeader.biBitCount = 24;
  pBmi->bmiHeader.biCompression = BI_RGB;
  BOOL bSetFormat = m_wndCap.SetVideoFormat(pBmi);
  delete [] pBmi;
  if (!bSetFormat)
  {
    CString sError;
    AfxMessageBox(_T("Could not set VFW capture format to 24 bit RGB"));
    return -1;
  }

  //Turn on preview and set preview rate to 15 fps
  m_wndCap.SetPreviewRate(67);
  m_wndCap.SetPreview(TRUE);

  //Delay the capture by m_nTimeInterval Milliseconds, This allows
  //time for the VFW device to initialize itself 
  m_nStartupTimerID = SetTimer(1, m_nTimeInterval, NULL);	

	return 0;
}

void CMainFrame::OnTimer(UINT nIDEvent) 
{
  if (nIDEvent == m_nStartupTimerID)
  {
    //Kill this timer as it is for once off
    KillTimer(m_nStartupTimerID);

    //Do the capture
    SendMessage(WM_COMMAND, ID_FILE_CAPTURE);
    ++m_nCurrentCapture;

    //Should we keep going
    if (m_nPeriodicInterval != -1)
      m_nPeriodicTimerID = SetTimer(2, m_nPeriodicInterval, NULL);
    else
      PostMessage(WM_CLOSE);
  }
  else if (nIDEvent == m_nPeriodicTimerID)
  {
    //Do the capture
    SendMessage(WM_COMMAND, ID_FILE_CAPTURE);
    ++m_nCurrentCapture;

    //Also should we shut down
    if (m_nMaxCaptures != -1)
    {
      if (m_nCurrentCapture >= m_nMaxCaptures)
        PostMessage(WM_CLOSE);
    }
  }
  else
	  CFrameWnd::OnTimer(nIDEvent);
}


void CMainFrame::OnDestroy() 
{
  //Kill the periodic timer if we had one
  if (m_nPeriodicTimerID)
    KillTimer(m_nPeriodicTimerID);

  //Let the parent class do its thing
	CFrameWnd::OnDestroy();
	
	
}
